Skip to main content

FNP - Mobile - WASM & Server-Side Proof Verification

Summary (Explain Like I’m 5)

Your phone is not as powerful as a server computer. Halo2 proof generation takes 1.2 milliseconds on server but 5+ seconds on phone - too slow for smooth editing! Solution: Your phone sends the data to the server, server generates the proof, server verifies it. Phone gets the result back instantly. Why it works: Proof verification is fast (16 milliseconds on phone). So we only move slow computation (proof generation) to the server. Your edits feel responsive!

Technical Deep Dive

Mobile WASM Constraint: Phone processing power: ~10% of server
  • Proof generation: 1.2ms (server) → 7.2ms (modern phone)
  • For interactive editing: <100ms feels instant; >500ms feels laggy
Challenge: Full protocol (M²-ORE + Kyber + Halo2 generation) too slow for mobile. Solution: Server-Side Proof Generation Architecture
Mobile Device (WASM):
  1. User types character "H"
  2. Generate LSEQ position (fast: <1ms)
  3. Kyber encapsulation (fast: <2ms)
  4. AES encryption of content (fast: <1ms)
  5. Send to server: {position, kyber_ct, encrypted_content}
  6. Wait for response (~50-100ms network)
  7. Receive: {proof, merged_document_state}
  8. Verify proof locally (fast: <16ms)
  9. Render to user

Server (Rust):
  1. Receive operation from mobile
  2. M²-ORE encrypt position (deterministic, no proof yet)
  3. Validate Lamport clock, LSEQ constraints
  4. Halo2 proof generation (1.2ms)
  5. Dilithium signature (fast: <5ms)
  6. Merge into document
  7. Return: {proof, merged_state}
  8. Send to other clients

Other Mobile Clients:
  1. Receive operation + proof
  2. Verify proof (fast: <16ms)
  3. M²-ORE comparison (deterministic)
  4. Decrypt content if authorized
  5. Apply CRDT merge
  6. Render
fnp-server (Rust Implementation)
pub struct ProofService {
    cache: HashMap<String, ProofResult>,
    prover: Halo2Prover,
}

impl ProofService {
    pub async fn generate_insert_proof(
        &self,
        witness: InsertWitness,
        public_input: PublicInput,
    ) -> Result<(InsertProof, u128)> {
        // Check cache first
        let cache_key = format!("{:?}", public_input);
        if let Some(cached) = self.cache.get(&cache_key) {
            return Ok((cached.proof.clone(), cached.time_ms));
        }

        // Generate proof
        let start = Instant::now();
        let proof = self.prover.prove_insert(witness, public_input)?;
        let time_ms = start.elapsed().as_millis();

        // Cache for 5 minutes
        self.cache.insert(cache_key, ProofResult {
            proof: proof.clone(),
            time_ms,
            timestamp: SystemTime::now(),
        });

        Ok((proof, time_ms))
    }
}

// REST API
POST /verify/insert
Request: {
    lseq_position: [encrypted_digits],
    kyber_ciphertext: bytes,
    content_ciphertext: bytes,
    lamport_clock: u32,
    replica_id: u32
}
Response: {
    halo2_proof: bytes (514),
    merged_document: DocumentState,
    time_ms: 1.2
}
WASM Module Capabilities (halo2/src/wasm.rs)
#[wasm_bindgen]
pub struct WasmProofVerifier {
    verifier: Halo2Verifier,
}

#[wasm_bindgen]
impl WasmProofVerifier {
    #[wasm_bindgen(constructor)]
    pub fn new() -> WasmProofVerifier {
        WasmProofVerifier {
            verifier: Halo2Verifier::new(),
        }
    }

    #[wasm_bindgen]
    pub fn verify_insert_proof(
        &self,
        proof_bytes: &[u8],
        public_input: &[u8],
    ) -> Result<bool> {
        let proof = InsertProof::from_bytes(proof_bytes)?;
        let public = PublicInput::from_bytes(public_input)?;

        // Fast verification: <16ms
        Ok(self.verifier.verify(&proof, &public)?)
    }

    #[wasm_bindgen]
    pub fn verify_delete_proof(
        &self,
        proof_bytes: &[u8],
        public_input: &[u8],
    ) -> Result<bool> {
        // Similar to insert
        Ok(self.verifier.verify_delete(proof_bytes, public_input)?)
    }
}

// JavaScript interface
export async function verifyProof(proof, publicInput) {
    const verifier = new WasmProofVerifier();
    return verifier.verify_insert_proof(
        new Uint8Array(proof),
        new Uint8Array(publicInput)
    );
}

Mermaid Diagrams

Key Terms

  • WASM → WebAssembly; compiles Rust/C to browser-executable bytecode
  • Server-Side Proof → Proof generation on server (1.2ms); mobile only verifies (16ms)
  • Proof Caching → Cache proofs for identical public inputs (5-minute TTL)
  • Interactive Latency → ~100ms round trip feels responsive; >500ms feels laggy
  • Batch Verification → Verify multiple proofs together (amortized speedup)
  • WASM Module → Compiled binary (~2MB) loaded into browser
  • REST Endpoint → /verify/insert, /verify/delete for server-side generation
  • Progressive Enhancement → Desktop: full local proof generation; Mobile: server-assisted

Q/A

Q: Why not always use server for proof generation? A: Consistency & auditability. Desktop clients want proof generation local (can’t be intercepted by server). Mobile clients use server assist due to resource constraints. Hybrid model: optional server-side for mobile users. Q: How is proof caching secure? A: Caching is keyed by deterministic public input hash. Same input always produces same proof (deterministic algorithm). If inputs differ by 1 bit, proof cache misses. Cache doesn’t reduce security, just latency. Q: Can server cheat by generating wrong proofs? A: No. WASM proof verifier (on mobile) is trustless. Proof either verifies or doesn’t. If server sends invalid proof, verification fails. Operation is rejected locally. No trust required. Q: What’s WASM binary size? A: ~2-5MB for full FNP WASM module (M²-ORE + Kyber + Halo2 verification + LSEQ). Reasonable for modern web apps. Compression reduces to ~600KB-1MB over HTTPS. Q: How does offline editing work with server-side proofs? A: 1) Offline: generate operations locally (no proof yet), store in IndexedDB. 2) Come online: sync operations to server. 3) Server generates proofs + verifies. 4) Mobile verifies proofs offline if preloaded. 5) Merge into document. Q: Why only 16ms for proof verification on mobile? A: Verification is O(log n) ≈ 16 pairings. Modern phones: ~1ms per pairing on GPU-accelerated libraries. 16 pairings × 1ms = 16ms. Proof generation is O(n) ≈ 7+ seconds (not mobile-friendly).

Example / Analogy

Notary Service Analogy: You want to notarize a document (like proof verification):
  • Desktop user: Has a notary public in their office. Generates + verifies proofs locally. <1 second
  • Mobile user: No notary in pocket. Uses traveling notary service (server). Calls ahead with document. Notary verifies + returns certificate. ~2 minutes round trip
Real-world FNP Mobile Scenario:
  1. Alice on iPhone editing proposal:
    • Types “The deadline is Friday” into FNP app
    • App generates position/content locally (fast)
    • App sends to server: “user_alice, position, encrypted_content”
  2. Server (1.2ms):
    • Generates Halo2 proof proving position + content valid
    • Signs with Dilithium
    • Merges into document state
  3. Server responds:
    • Returns: proof + merged document to Alice
  4. Alice’s phone (16ms):
    • Verifies proof (fast WASM code)
    • Decrypts merged document
    • Renders: “The deadline is Friday” with character green ✓
  5. Bob on Android:
    • Receives proof + encrypted text from server
    • Verifies proof
    • Can’t decrypt (doesn’t have Alice’s Kyber key)
    • Sees encrypted placeholder until Alice shares key

Cross-References: System Overview, Kyber-1024, Halo2 Circuits, FNP Protocol Flow Category: Mobile | Performance | Infrastructure | WASM Difficulty: Advanced ⭐⭐⭐⭐ Updated: 2025-11-28